package com.zkh.myutils.expression;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import com.zkh.myutils.utils.StringUtils;

/**
 * 元素项
 */
public class Element {
	
	/**
	 * 构造方法
	 * @param value 值
	 * @param type 类型
	 */
	Element(Object value, Type type){
		this.type = type;
		//保存值
		if(type==Type.NUMBER) {
			try {
				this.numberValue = value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
			}catch (NumberFormatException e) {
				throw new ExpressionException("数字格式化失败：" + value);
			}
		}//布尔值
		else if(type==Type.BOOLEAN) {
			this.booleanValue = value instanceof Boolean ? (Boolean) value : "true".equals(value);
		}//操作符
		else if(type==Type.OPERATOR) {
			this.operator = (char) value;
		}//表达式
		else if(type==Type.EXPRESSION) {
			this.expression = Expression.newInstance(value.toString(), false);
		}//变量值
		else if(type==Type.OBJECT) {
			this.object = value;
		}//函数
		else if(type==Type.FUNCTION) {
			this.functionName = (char) value;
			//初始化函数参数元素
			functionElement = new ArrayList<>();
		}//其它
		else {
			this.stringValue = value==null ? null : value.toString();
		}
	}
	
	/**
	 * 元素类型
	 */
	private Type type;
	/**
	 * 字符串值
	 */
	private String stringValue;
	/**
	 * 数字值
	 */
	private BigDecimal numberValue;
	/**
	 * 布尔值
	 */
	private Boolean booleanValue;
	/**
	 * 对象值
	 */
	private Object object;
	/**
	 * 操作符
	 */
	private Character operator;
	/**
	 * 函数名
	 */
	private Character functionName;
	/**
	 * 函数描述
	 */
	private String functionDesc;
	/**
	 * 表达式
	 */
	private Expression expression;
	/**
	 * 函数参数元素
	 */
	private List<Element> functionElement;
	
	//临时变量
	private int index;
	
	/**
	 * 获取值
	 * @return
	 */
	public Object getValue() {
		switch (type) {
			case NUMBER:
				return numberValue;
			case BOOLEAN:
				return booleanValue;
			case OPERATOR:
				return operator;
			case OBJECT:
				return object;
			case EXPRESSION:
				return expression;
			case FUNCTION:
				return "$" + functionName + "(" + functionDesc + ")";
			default:
				return stringValue;
		}
	}
	public Type getType() {
		return type;
	}
	public void setType(Type type) {
		this.type = type;
	}
	public String getStringValue() {
		return stringValue;
	}
	public void setStringValue(String stringValue) {
		this.stringValue = stringValue;
	}
	public BigDecimal getNumberValue() {
		return numberValue;
	}
	public void setNumberValue(BigDecimal numberValue) {
		this.numberValue = numberValue;
	}
	public Boolean getBooleanValue() {
		return booleanValue;
	}
	public void setBooleanValue(Boolean booleanValue) {
		this.booleanValue = booleanValue;
	}
	public char getOperator() {
		return operator;
	}
	public Character getFunctionName() {
		return functionName;
	}
	/**
	 * 获取表达式值
	 * @param map 参数
	 * @return
	 */
	public Object getExpressionValue(Map<String, ? extends Object> map) {
		return expression.execute(map);
	}
	/**
	 * 解析函数描述（IN() CASE() DECODE() LENGTH() EMPTY()）
	 * @param functionValue 函数描述
	 * @return
	 */
	public Element renderFunctionValue(String functionValue) {
		//保存描述
		this.functionDesc = functionValue;
		//转字符
		char[] chars = functionValue.toCharArray();
		//解析参数
		List<String[]> paramList = parseParam(chars);
		//遍历
		for(String[] param: paramList) {
			//值
			String v = param[0];
			//表达式
			if("1".equals(param[1])) {
				functionElement.add(new Element(v, Type.EXPRESSION));
			}//字符串
			else if("2".equals(param[1])) {
				functionElement.add(new Element(v, Type.STRING));
			}//函数
			else if(v.startsWith("$")) {
				//函数元素
				Element f = new Element(v.charAt(1), Type.FUNCTION);
				//解析参数
				f.renderFunctionValue(v.substring(3, v.length()-1));
				//保存
				functionElement.add(f);
			}
			//数字
			else if(StringUtils.isNumeric(v)){
				functionElement.add(new Element(v, Type.NUMBER));
			}//布尔值
			else if("true".equals(v) || "false".equals(v)) {
				functionElement.add(new Element("true".equals(v), Type.BOOLEAN));
			}//NULL
			else if("null".equals(v)) {
				functionElement.add(new Element(null, Type.NULL));
			}
			//变量
			else {
				functionElement.add(new Element(param[0], Type.VARIABLE));
			}
		}
		//返回当前对象
		return this;
	}
	
	/**
	 * 解析函数参数
	 * @param chars 参数字符串，不含括号
	 * @return 每个参数的字符和是否是操作符标识
	 */
	private static List<String[]> parseParam(char[] chars){
		//参数串
		List<String[]> params = new ArrayList<>();
		/** 以逗号拆分参数（需要考虑嵌套函数和字符串） **/
		//计括号数量、引号、转义符数量、操作符标识、函数标识、字符串标识、函数类型
		int brkt = 0, qut = 0, s = 0, oper=0, funInd = 0, str = 0, tFun = 0;
		//临时字符串
		StringBuilder sb = new StringBuilder();
		//遍历
		for(char curr: chars) {
			//函数
			if(curr=='$') {
				funInd = 1; tFun = 1;
			}//括号
			else if(curr=='(') {
				//括号累加
				brkt++;
				//如果不是函数，也不是字符串，就计操作符
				if(funInd==0 && oper==0 && str==0) {
					oper = 1;
				}
			}else if(curr==')') {
				//括号累减
				brkt--;
				//函数标识
				if(funInd==1 && brkt==0) {
					funInd = 0;
				}
			}//引号
			else if(curr=='\'' || curr=='"') {
				//开始引号
				if(qut==0){
					qut = curr;
					//标记字符串
					str = 1;
				}//结束字符
				else if(qut==curr) {
					//转义符为偶数，则视为没有转义
					if(s%2==0) {
						qut = 0;
					}//转义符为基数，则没有结束
					else {
						//移除前面的转义符
						sb.setLength(sb.length()-1);
					}
				}
			}//操作符
			else if(qut==0 && oper==0 && Expression.getOperLvl(curr)>0) {
				oper = 1;
			}
			//逗号
			else if(curr==',' && brkt==0 && qut==0 && funInd==0) {
				//字符串类型
				boolean isStr = str==1 && oper==0;
				//保存（优先级：表达式>函数>字符串>其它）
				if(oper==1) {
					params.add(new String[] {sb.toString(), "1"});
				}else if(tFun==1) {
					params.add(new String[] {sb.toString(), "0"});
				}else if(isStr) {//字符串
					params.add(new String[] {sb.substring(1, sb.length()-1), "2"});
				}else {//其它
					params.add(new String[] {sb.toString(), String.valueOf(oper)});
				}
				//重置操作符标识、字符串标识和函数标识
				oper = 0; str = 0; tFun = 0;
				//清空
				sb.setLength(0);
				//继续下个字符
				continue;
			}
			//判断字符串转义符号
			if(curr=='\\' && qut!=0) {
				s++;
			}else if(s>0) {
				s=0;
			}
			//保存
			sb.append(curr);
		}
		//还有字符串
		if(sb.length()>0) {
			//字符串类型
			boolean isStr = str==1 && oper==0;
			//保存
			params.add(new String[] {isStr ? sb.substring(1, sb.length()-1) : sb.toString(), isStr ? "2" : String.valueOf(oper)});
		}
		//返回解析后参数
		return params;
	}
	
	/**
	 * 获取变量
	 * @param i 当前索引
	 * @param len 字符总长度
	 * @param chars 字符列表
	 * @param sb 临时字符容器
	 * @return
	 */
	static Element getVariable(int i, int len, char curr, char[] chars, StringBuilder sb, Function<Character, Boolean> brkCheck) {
		//下一字符
		char next = i+1>=len ? '\0' : chars[i+1];
		//循环遍历
		while(next!='\0') {
			//操作符，跳出循环
			if(brkCheck.apply(next)) {
				break;
			}//变量。类型有（a，b[0]，b['c']，b["d"]，user.age，u['age'][0]）
			else if(next=='_' || next>='0' && next<='9' || next>='a' && next<='z' || next>='A' && next<='Z' || 
					next=='[' || next==']' || next=='.' || next=='\'' || next=='"') {
				//保存
				sb.append(next);
				//索引加1
				i++;
				//下一个字符
				next = i+1>=len ? '\0' : chars[i+1];
			}else {
				throw new ExpressionException("非法字符：" + sb.toString() + next);
			}
		}
		//字符
		String t = sb.toString();
		//创建Element对象
		Element element = "true".equals(t) || "false".equals(t) ? new Element("true".equals(t), Type.BOOLEAN) : ("null".equals(t) ? new Element(null, Type.NULL) : new Element(t, Type.VARIABLE));
		//保存索引
		element.setIndex(i);
		//返回元素
		return element;
	}
	
	/**
	 * 获取字符串
	 * @param i 当前索引
	 * @param len 字符总长度
	 * @param chars 字符列表
	 * @param sb 临时字符容器
	 * @param onlyRead 只读取，不处理
	 * @return
	 */
	public static int getString(int i, int len, char curr, char[] chars, StringBuilder sb, boolean onlyRead) {
		//转义符数量
		int s = 0;
		//下一字符
		char next = i+1>=len ? '\0' : chars[i+1];
		//循环遍历
		while(next!='\0') {
			//指针后移
			i++;
			//结束符
			if(next==curr) {
				//转义符为偶数，真正结束
				if(s%2==0) {
					break;
				}//转义符为基数，则没有结束
				else {
					//只读取，不处理
					if(onlyRead) {
						sb.append(next);
					}//处理转义符
					else {
						//移除前面的转义符，添加当前字符
						sb.setCharAt(sb.length()-1, next);
					}
					//重置转义符
					s = 0;
				}
			}//不是结束符
			else {
				//保存字符
				sb.append(next);
				//转义符计数
				if(next=='\\') {
					s++;
				}else if(s>0) {
					s=0;
				}
			}
			//下一个字符
			next = i+1>=len ? '\0' : chars[i+1];
		}
		//返回索引
		return i;
	}
	
	/**
	 * 获取数字
	 * @param i 当前索引
	 * @param len 字符总长度
	 * @param chars 字符列表
	 * @param sb 临时字符容器
	 * @return
	 */
	static int getNumber(int i, int len, char[] chars, StringBuilder sb) {
		//下一字符
		char next = i+1>=len ? '\0' : chars[i+1];
		//循环遍历
		while(next!='\0') {
			//数字
			if(next>='0' && next<='9' || next=='.') {
				//追加
				sb.append(next);
				//索引加1
				i++;
				//下一个字符
				next = i+1>=len ? '\0' : chars[i+1];
			}else {
				break;
			}
		}
		//返回索引
		return i;
	}
	
	/**
	 * 解析函数
	 * @param i 当前索引
	 * @param len 字符总长度
	 * @param chars 字符列表
	 * @param sb 临时字符容器
	 * @return
	 */
	static Element parseFunction(int i, int len, char[] chars, StringBuilder sb) {
		//函数名
		char curr = chars[++i];
		//保存
		Element fun = new Element(curr, Type.FUNCTION);
		//格式错误
		if(i+1>=len || chars[i+1]!='(') {
			throw new ExpressionException("函数格式错误，必须包含在()内");
		}
		//移动指针
		i++;
		//下一字符
		char next = i+1>=len ? '\0' : chars[i+1];
		//统计括号
		int count = 1;
		//循环遍历
		while(next!='\0' && count>0) {
			//左括号
			if(next=='(') {
				count++;
			}else if(next==')') {
				count--;
			}
			//索引加1
			i++;
			//不是结束符
			if(next!=')' || count>0) {
				//追加
				sb.append(next);
				//下一个字符
				next = i+1>=len ? '\0' : chars[i+1];
			}
		}
		//保存索引
		fun.setIndex(i);
		//解析函数值
		fun.renderFunctionValue(sb.toString());
		//返回函数对象
		return fun;
	}
	
	public List<Element> getFunctionElement() {
		return functionElement;
	}
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
	public Object getObject() {
		return object;
	}
	@Override
	public String toString() {
		return "" + getValue();
	}
}
